# POP3 API Module 
import threading
import asyncore
import asynchat
import socket
import os
import time
from email.message import Message
import listenboard
import plobject
import plex

# the dummy data returned by server when LIST and RETR commands are issued
#LIST_RESP = '1 1\r\n2 2\r\n3 3\r\n4 4\r\n5 5\r\n.\r\n'
#LIST_RESP = '1 1\r\n'
RETR_RESP = """From: plexus@relationalspace.org\
\r\nContent-Type: text/plain\r\n\
MIME-Version: 1.0\r\n\
Subject: Dummy\r\n\
\r\n\
line1\r\n\
line2\r\n\
line3\r\n\
.\r\n"""

class APIPOP3Handler(asynchat.async_chat):

    def __init__(self, conn):
        asynchat.async_chat.__init__(self, conn)
        self.set_terminator("\r\n")
        self.in_buffer = []
        self.push('+OK Plexus API POP3 server ready.')
        print('+OK Plexus API POP3 server ready.')

    def collect_incoming_data(self, data):
        #print("collect_incoming_data")
        self.in_buffer.append(data)

    def found_terminator(self):
        line = ''.join(self.in_buffer)
        self.in_buffer = []
        cmd = line.split(' ')[0].lower()
        space = line.find(' ')
        if space != -1:
            arg = line[space + 1:]
        else:
            arg = ""
        print('cmd: ' + cmd + ' arg: ' + arg)
        if hasattr(self, 'cmd_' + cmd):
            method = getattr(self, 'cmd_' + cmd)
            method(arg)
        else:
            self.push('-ERR unrecognized POP3 command "%s".' %cmd)
            print('-ERR unrecognized POP3 command "%s".' %cmd)
            
    def handle_error(self):
        raise

    def push(self, data):
        asynchat.async_chat.push(self, data + '\r\n')

    def cmd_echo(self, arg):
        # sends back the received string (used by the test suite)
        self.push(arg)
        print('ECHO')

	# We don't look at the username
    def cmd_user(self, arg):
        self.push('+OK no password required - but feel free')
        print('USER +OK no password required - but feel free')

	# Or the password        
    def cmd_pass(self, arg):
        num_msgs = listenboard.count_listened()
        #num_msgs = 100
        self.push('+OK ' + str(num_msgs) + ' messages')
        print('+OK ' + str(num_msgs) + ' messages')
        
    def cmd_stat(self, arg):
        num_msgs = listenboard.count_listened()
        total_size = 0
        loop_count = 0
        while (loop_count < num_msgs):
        	total_size = total_size + listenboard.size_listened(loop_count + 1)
        	loop_count = loop_count + 1
        #num_msgs = 100
        self.push('+OK ' + str(num_msgs) + ' ' + str(total_size)) # Not accurate, but so what?
        print('+OK ' + str(num_msgs) + ' ' + str(total_size))        

    def cmd_rset(self, arg):
        self.push('+OK RESET')
        print('STAT +OK RESET')   

    def cmd_list(self, arg):
        if arg:
        	msg_size = listenboard.size_listened(int(arg))
        	self.push('+OK %s %s' %(arg, msg_size))
        	#print('LIST +OK %s %s' %(arg, msg_size))
        else:
            self.push('+OK mailbox scan listing follows')
            print('+OK mailbox scan listing follows')
            num_msgs = listenboard.count_listened() 
            #if (num_msgs > 100):
            #	num_msgs = 100		# Limited for testing
            starting = 0
            #num_msgs = 101
            while (starting < num_msgs):
            	msg_size = listenboard.size_listened(starting+1)
            	self.push('%s %s' %(starting+1, msg_size))
            	#print('%i %i' %(starting+1, msg_size))
            	starting = starting + 1
            self.push(".\r\n")
            #print(".\r\n")
            
    def cmd_uidl(self, arg):
        if arg:
        	thingy = listenboard.get_listened(arg)
        	unique = int((thingy.time * 1000L) + len(thingy.plob))
        	self.push('+OK %s %012s' %(arg, unique))
        	#print('UIDL +OK %s %s' %(arg, unique))
        else:
            self.push('+OK mailbox scan listing follows')
            print('+OK mailbox scan listing follows')
            starting = 0
            num_msgs = listenboard.count_listened() 
            while (starting < num_msgs):
				thingy = listenboard.get_listened(starting+1)
				unique = int((thingy.time * 1000L) + len(thingy.plob))
				self.push('%s %012s' %(starting+1, unique))
				starting = starting + 1
            	#print('%i %i' %(starting+1, unique))
            self.push(".\r\n")
            #print(".\r\n")

    def cmd_retr(self, arg):
    	thingy = listenboard.get_listened(int(arg))  # returns plobject
    	#print(thingy)
    	msg = Message()		# Make a new message
    	name = plex.pluid_to_name(thingy.pluid)
    	clean_plob = thingy.plob.encode('utf-8')
    	lastname = name[1]
    	msg['From'] = name[0] + ' ' + lastname + ' <' + thingy.pluid + '@plexus.relationalspace.org>'
    	#msg.set_unixfrom('')
    	msg['Subject'] = '[' + thingy.id + '] ' + thingy.title
    	timestruct = time.localtime(thingy.time)
    	msg['Date'] = time.strftime('%B %d, %Y %H:%M:%S' ,timestruct)
    	msg['Content-Type'] = thingy.type
    	msg.set_payload(clean_plob)
    	print(msg.__str__())
        self.push('+OK %s bytes' %len(msg.as_string()))
        #print('RETR +OK %s bytes' %len(msg.as_string()))
        asynchat.async_chat.push(self, msg.as_string())
        self.push('\r\n.\r\n')

    #cmd_top = cmd_retr

    def cmd_dele(self, arg):
        self.push('+OK message marked for deletion.')
        #print('DELE +OK message marked for deletion.')
 
    def cmd_quit(self, arg):
        self.push('+OK Plexus API POP3 Server saying Good-Bye.')
        print('QUIT +OK Plexus API POP3 Server saying Good-Bye.')
        self.close()

    def cmd_noop(self, arg):
        self.push('+OK done nothing.')
        #print('NOOP +OK done nothing.')
        
    def cmd_rpop(self, arg):
        self.push('+OK done nothing.')
        #print('RPOP +OK done nothing.')

class POP3APIServer(asyncore.dispatcher):

    handler = APIPOP3Handler

    def __init__(self, address, af=socket.AF_INET):
        asyncore.dispatcher.__init__(self)
        self.create_socket(af, socket.SOCK_STREAM)
        self.bind(address)
        self.listen(5)
        self.host, self.port = self.socket.getsockname()[:2]

    def handle_accept(self):
    	print('handle_accept')
    	print self.handler
        conn, addr = self.accept()
        #self.handler = self.handler(conn)
        self.handler = APIPOP3Handler(conn)
        #print('Closing APIPOP3Server ACCEPT')
        #self.close()

    def handle_connect(self):
 		print('handle_connect')

    def handle_close(self):
 		print('handle_close')
 		self.close()
        
    handle_read = handle_connect

    def writable(self):
        return 0

    def handle_error(self):
        raise

def POP3thread():
	#This is the 2.2 way of running asynchronous servers
	print ('Starting POP3 server on port 4183...')	
	server = POP3APIServer(("localhost", 4183))
	print('POP3 server started.')
	while True:
		asyncore.loop()	

# Basic funtion that all API modules must implement
def APIStart(stuff):
	apithread = threading.Thread(target=POP3thread)
	apithread.start()

if __name__ == '__main__':
    print('This is the POP3 API module.')
    APIStart(None)
